package com.corx.network.mysqlstorage.load;

import com.corx.network.config.properties.NetworkConfig;
import com.corx.network.core.exception.NetworkException;
import com.corx.network.core.listener.DefaultReceivedMessageListener;
import com.corx.network.core.listener.ReceivedMessageListener;
import com.corx.network.core.load.NetworkLoad;
import com.corx.network.core.manager.NetworkManager;
import com.corx.network.core.manager.ReceivedMessageListenerJarManager;
import com.corx.network.core.model.ReceivedMessage;
import com.corx.network.core.network.MqttNetworkClient;
import com.corx.network.core.type.NetworkStateEnum;
import com.corx.network.core.type.NetworkTypeEnum;
import com.corx.network.mysqlstorage.common.Constants;
import com.corx.network.mysqlstorage.hub.DeviceRepositoryHub;
import com.corx.network.mysqlstorage.model.*;
import com.corx.network.mysqlstorage.util.ConfigUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author gx
 * @version 1.0.0
 */
@Slf4j
public class JdbcNetworkLoad implements NetworkLoad {


    private final DeviceRepositoryHub repositoryHub;

    private final ReceivedMessageListenerJarManager jarManager;

    private final NetworkManager networkManager;

    public JdbcNetworkLoad(ReceivedMessageListenerJarManager jarManager, NetworkManager networkManager, DeviceRepositoryHub repositoryHub) {
        this.networkManager = Objects.requireNonNull(networkManager, "NetworkManager 注入不能为空");
        this.repositoryHub = Objects.requireNonNull(repositoryHub, "DeviceRepositoryHub 注入不能为空");
        this.jarManager = Objects.requireNonNull(jarManager, "JarManager 注入不能为空");
    }

    @Override
    public void initNetworks() {
        List<Network> all = repositoryHub.networkRepo().findAll();

        List<Network> errorNetworks = new ArrayList<>();

        for (Network network : all) {
            try {
                initNetwork(network);
            } catch (Exception e) {
                log.warn("装载和启动网络组件发生异常，组件：network={} ", network, e);
                if (network.getId() != null && network.getType() != null && !network.getType().isEmpty()) {
                    NetworkTypeEnum networkTypeEnum = NetworkTypeEnum.getByType(network.getType());
                    if (networkTypeEnum != null) {
                        networkManager.destroyNetwork(networkTypeEnum, network.getId().toString());
                    }
                }
                network.setState(NetworkStateEnum.ERROR.getState());
                network.setStatus(Constants.NetworkStatus.INACTIVE);
                errorNetworks.add(network);
            }

        }
        if (!errorNetworks.isEmpty()) {
            repositoryHub.networkRepo().updateBatch(errorNetworks);
        }

    }

    public void initNetwork(Network network) throws NetworkException {
        NetworkConfig config = ConfigUtil.parseConfigJson(network);
        com.corx.network.core.network.Network networkCore = networkManager.registerNetwork(config);
        if (Constants.NetworkStatus.ACTIVE.equals(network.getStatus())) {
            networkCore.connect();
            loadPointConfig(networkCore, network);
        }
    }


    private void loadPointConfig(com.corx.network.core.network.Network coreNetwork, Network network) throws NetworkException {
        try {
            ReceivedMessageListener<ReceivedMessage, ReceivedMessage> listener = getListener(network);
            if (NetworkTypeEnum.MQTT_CLIENT == NetworkTypeEnum.getByType(network.getType())) {
                List<DevicePointParam> pointConfig = getPointConfig(network);
                for (DevicePointParam pointParam : pointConfig) {
                    MqttDevicePointParam mqttDevicePointParam = (MqttDevicePointParam) pointParam;
                    MqttNetworkClient mqttNetworkClient = (MqttNetworkClient) coreNetwork;
                    mqttNetworkClient.subscribe(
                        mqttDevicePointParam.getTopic(),
                        (mqttDevicePointParam.getQos() == null ? 0 : mqttDevicePointParam.getQos()),
                        listener
                    );
                }
            } else {
                throw new NetworkException(String.format("无法识别的网络类型 ，NetworkType = %s ， Network = %s", network.getId(), network));
            }
        } catch (Exception e) {
            throw new NetworkException(String.format("加载设备端点配置发生了异常 ，NetworkType = %s ， Network = %s", network.getId(), network), e);
        }
    }

    private ReceivedMessageListener<ReceivedMessage, ReceivedMessage> getListener(Network network) {
        ReceivedMessageListener<ReceivedMessage, ReceivedMessage> listener = jarManager.getListener(network.getId().toString());
        if (listener == null) {
            log.warn("在 jarManager 中未能找到 listener ，已使用默认的DefaultReceivedMessageListener ，network = {}", network);
            listener = new DefaultReceivedMessageListener();
        }
        return listener;
    }

    private List<DevicePointParam> getPointConfig(Network network) throws NetworkException {
        List<DevicePointParam> pointParams = new ArrayList<>();
        List<Device> devices = repositoryHub.deviceRepo().findByNetworkId(network.getId());
        List<Long> deviceIds = devices.stream().map(Device::getId).collect(Collectors.toList());
        List<DevicePoint> devicePoints = repositoryHub.devicePointRepo().findByDeviceIds(deviceIds);
        for (DevicePoint devicePoint : devicePoints) {
            try {
                if (!Constants.DevicePointStatus.ACTIVE.equals(devicePoint.getStatus())) {
                    continue;
                }
                String paramsJson = devicePoint.getParams();
                if (paramsJson == null || paramsJson.isEmpty()) {
                    log.warn("设备端点params为空，跳过订阅 ，devicePoint = {}", devicePoint);
                    continue;
                }
                NetworkTypeEnum typeEnum = NetworkTypeEnum.getByType(network.getType());
                if (NetworkTypeEnum.MQTT_CLIENT == typeEnum) {
                    MqttDevicePointParam mqttParam = (MqttDevicePointParam) ConfigUtil.parseParam(typeEnum, paramsJson);
                    pointParams.add(mqttParam);
                }

            } catch (Exception e) {
                throw new NetworkException(String.format("设备端点params解析异常 ，params = %s ", devicePoint.getParams()), e);
            }
        }

        return pointParams;
    }


}
